package Model;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Point;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

/**
 * Cette class permet de définir un motif en générale pour un dessin.
 * 
 * @author Kilian Cousein et Benjamin Tardieu
 */
public abstract class Motif
{	
//======================================================================//
//============================ Variables ===============================//
//======================================================================//
	
	protected int x; // coordonnée x
	protected int y; // coordonnée y

	protected int largeurBordure = 0; // largeur de la bordure
	protected Color couleurBordure = Color.black; // couleur de la bordure
	
	protected Color couleur; // couleur du motif
	protected float opacite = 1.0f; // opacité du motif
	protected float opaciteBordure = 1.0f; // opacité de la bordure
	
    protected boolean selectionne = false; // cette variable permet de sélectionner le motif
    
	protected int poigneeSelectionne = 0; // cette variable permet de savoir quelle poignée est sélectionnée
	
	protected static final int HANDLE_SIZE = 9; // définit la taille des poignées
	
	/* Définition des différentes poignées */
	public final static int AUCUNE_POIGNEE = 0;
	public final static int GAUCHE_HAUT_POIGNEE = 1;
	public final static int DROITE_HAUT_POIGNEE = 2;
	public final static int GAUCHE_BAS_POIGNEE = 3;
	public final static int DROITE_BAS_POIGNEE = 4;
	public final static int GAUCHE_POIGNEE = 5;
	public final static int HAUT_POIGNEE = 6;
	public final static int DROITE_POIGNEE = 7;
	public final static int BAS_POIGNEE = 8;
	
	
//======================================================================//
//========================== Constructeurs =============================//
//======================================================================//
	
	/**
	 * Crée une nouvelle instance de <i>Motif</i> par défaut.
	 */
	public Motif()
	{
		this.x = -1;
		this.y = -1;
	}
	
	
	/**
	 * Crée une nouvelle instance de <i>Motif</i> qui sera un clone de <i>motif</i>.
	 * 
	 * @param motif Motif
	 */
	public Motif(Motif motif)
	{	
		this.x = motif.x; 
		this.y = motif.y;
		
		this.couleurBordure = motif.couleurBordure;
		this.largeurBordure = motif.largeurBordure;
		
		this.opacite = motif.opacite;
		this.opaciteBordure = motif.opaciteBordure;
		
		this.couleur = motif.couleur;
		selectionne = motif.selectionne;
	}
	
	
//======================================================================//
//======================== Méthodes abstraites =========================//
//======================================================================//

	/**
	 * Cette méthode permet de dessiner un motif dans la fenêtre grâce à <i>g</i>.
	 * 
	 * @param g Variable permettant de dessiner dans une fenêtre.
	 */
 	public abstract void dessinerMotif(Graphics2D g);
 	
 	
 	/**
 	 * Cette méthode permet de dessiner les poignées d'un motif dans la fenêtre grâce à <i>g</i>.
 	 * 
 	 * @param g Variable permettant de dessiner dans une fenêtre.
 	 */
    public abstract void dessinerPoignees(Graphics2D g);
    
    
    /**
     * Cette méthode permet appliquer les modifications nécessaires lors du déplacement d'un motif par l'utilisateur.
     * 
     * @param x Valeur du déplacement du motif sur l'axe des abscisses.
     * @param y	Valeur du déplacement du motif sur l'axe des ordonnées.
     */
    public abstract void deplacer(int x, int y);
    
    
    /**
     * Cette méthode permet appliquer les modifications nécessaires lors du redimensionner d'un motif par l'utilisateur.
     * 
     * @param x1 Ancienne coordonnée de l'axe des abscisses du motif avant sa redimensionnement. 
     * @param x2 Nouvelle coordonnée de l'axe des abscisses du motif après sa redimensionnement.
     * @param y1 Ancienne coordonnée de l'axe des ordonnées du motif avant sa redimensionnement. 
     * @param y2 Nouvelle coordonnée de l'axe des ordonnées du motif après sa redimensionnement.
     */
 	public abstract void redimensionner(int x1, int x2, int y1, int y2);
    
 	
 	/**
 	 * Cette méthode permet de savoir si le <i>point</i> est dans le motif.
 	 * 
 	 * @param point Coordonnée du point duquel on veut savoir si il est dans le motif ou non.
 	 * @return vrai si le <i>point</i> est dans le motif, sinon faux. 
 	 */
	public abstract boolean contient(Point point);
	
	
	/**
	 * Cette méthode permet de faire correspondre les valeurs des motifs dessinés par l'utilisateur lors de la création de ce dernier au valeur de ce motif. 
	 * 
	 * @param x1 Coordonnée de l'axe des abscisses du début du motif lors de sa création. 
	 * @param y1 Coordonnée de l'axe des ordonnées du début du motif lors de sa création. 
	 * @param x2 Coordonnée de l'axe des abscisses courant (souris).
	 * @param y2 Coordonnée de l'axe des ordonnées courant (souris).
	 */
 	public abstract void faireCorrespondre(int x1, int y1, int x2, int y2);
 	
 	
 	/**
 	 * Cette méthode permet de savoir dans quel poignée l'utilisateur a cliqué lors du redimensionnement du motif.
 	 * 
 	 * @param point Point que l'on veut executer cette méthode.
 	 */
 	public abstract void findHandles(Point point);
 	
 	
 	/**
 	 * Cette méthode permet de changer un <i>Motif</i> en format svg pour pouvoir enregistrer un dessin.
 	 * 
 	 * @param doc Document où on va enregistrer le code svg.
 	 * @return un <i>Element</i> correspondant au code svg du motif.
 	 */
	public abstract Element enregistrer(Document doc);
	
	
	/**
	 * Cette méthode permet de transformer un fichier svg en <i>Motif</i>, ce qui permet d'ouvir en fichier svg et de pouvoir le modifier.
	 * 
	 * @param node Texte du fichier svg.
	 */
	public abstract void ouvrir(Node node);
	
	
	/**
	 * Cette méthode permet de retourner la poignée que l'utilisateur s'est servi pour redimensionner le motif.
	 * 
	 * @param x Les coordonnées de l'axe des abscisses pour savoir dans quelle poignée l'utilisateur s'est servi pour redimensionner le motif.
	 * @param y Les coordonnées de l'axe des ordonnées pour savoir dans quelle poignée l'utilisateur s'est servi pour redimensionner le motif.
	 * @return La poignée que l'utilisateur s'est servi pour redimensionner le motif.
	 */
 	public abstract int getHandles(int x, int y);
 	
	
	/**
	 * Cette méthode permet de cloner le motif.
	 * 
	 *  @return Le clone du Motif.
	 */
	public abstract Motif clone();
	
	
	/**
	 * Cette méthode permet de créer un nouveau <i>Motif</i> avec la couleur du motif et de la bordure aux couleurs courantes.
	 * 
	 * @return Un nouveau <i>Motif</i>.
	 */
	public abstract Motif nouvelleInstance();
	
	
//======================================================================//
//============================= Méthodes ===============================//
//======================================================================//
	
	/**
	 * Cette méthode permet de vérifier si deux motifs sont égaux.
	 * 
	 *  @param o Le motif qui doit être comparé au motif.
	 *  @return Vrai si les motifs sont égaux, sinon faux.
	 */
	public boolean equals(Object o)
	{
		Motif motif = (Motif) o;
		
		if(this.getClass() == motif.getClass())
		{
			boolean egale = true;
			
			egale &= this.x == motif.x;
			egale &= this.y == motif.y;
			egale &= this.largeurBordure == motif.largeurBordure;
			egale &= this.couleurBordure == motif.couleurBordure;
			egale &= this.opacite == motif.opacite;
			egale &= this.opaciteBordure == motif.opaciteBordure;
			egale &= this.couleur == motif.couleur;
			
			return egale;
		}
		else
			return false;
	}
	
	
	/** 
	 * Cette méthode permet de décrire un objet <i>Motif</i>.
	 * 
	 * @return Une chaîne de caractère décrivant un motif.
	 */
	public String toString()
	{
		String texte = super.toString()+"\n";
		texte += "x: "+ this.x +"\n";
		texte += "y: "+ this.y +"\n";
		texte += "largeur bordure: "+this.largeurBordure+"\n";
		texte += "couleur bordure: "+this.couleurBordure+"\n";
		texte += "opacite couleur: "+this.opacite+"\n";
		texte += "opacite bordure: "+this.opaciteBordure+"\n";
		texte += "couleur: "+ this.couleur +"\n";
		texte += "selectionne: "+ this.selectionne +"\n";
		
		return texte;
	}

	
	/**
	 * Cette méthode permet de dessiner un <i>Motif</i> dans la fenêtre.
	 * 
	 * @param g Variable permettant de dessiner dans une fenêtre.
	 */
	public void dessiner(Graphics2D g) 
	{
		dessinerMotif(g);
		
		if (this.getSelectionne())
			this.dessinerPoignees(g);
	}
	
	
	/**
	 * Cette méthode convertit un <i>String</i> en <i>Color</i>.  
	 * 
	 * @param couleurRGB Couleur décrite en par un <i>String</i>.
	 * @return La couleur correspondant à <i>couleurRGB</i>.
	 */
	public Color convertitStringCouleur(String couleurRGB)
	{
		Color couleur = null;		
		couleurRGB.replaceAll(" ", "");
		
		if(couleurRGB.matches("rgb\\([0-9]{1,3},[0-9]{1,3},[0-9]{1,3}\\)"))
		{
			String rgb[];	
			couleurRGB = couleurRGB.substring(4, couleurRGB.length() - 1);
			rgb = couleurRGB.split(",");
			
			couleur = new Color(Integer.valueOf(rgb[0]), Integer.valueOf(rgb[1]), Integer.valueOf(rgb[2]));
		}
		else
			System.out.println("Erreur lors du chargement de la couleur.");
		
		return couleur;
	}
	
	
//======================================================================//
//========================= Getters et Setters =========================//
//======================================================================//
	
	/**
	 * Retourne les coordonnées x du motif.
	 * 
	 * @return Les coordonnées x du motif.
	 */
	public int getX()
	{
		return x;
	}
	
	
	/**
	 * Cette méthode permet de modifier les coordonnées x du motif.
	 * 
	 * @param x Nouvelle valeur des coordonnées x du motif.
	 */
	public void setX(int x)
	{
		this.x = x;
	}
	
	
	/**
	 * Retourne les coordonnées y du motif.
	 * 
	 * @return Les coordonnées y du motif.
	 */
	public int getY()
	{
		return y;
	}
	
	
	/**
	 * Cette méthode permet de modifier les coordonnées y du motif.
	 * 
	 * @param y Nouvelle valeur des coordonnées y du motif.
	 */
	public void setY(int y)
	{
		this.y = y;
	}
	
	
	/**
	 * Retourne la largeur de la bordure du motif.
	 * 
	 * @return La largeur de la bordure du motif.
	 */
	public int getLargeurBordure()
	{
		return largeurBordure;
	}
	
	
	/**
	 * Cette méthode permet de modifier la largeur de la bordure du motif.
	 * 
	 * @param largeurBordure Nouvelle valeur de la largeur de la bordure du motif.
	 */
	public void setLargeurBordure(int largeurBordure)
	{
		this.largeurBordure = largeurBordure;
	}
	
	
	/**
	 * Retourne la couleur de la bordure du motif.
	 * 
	 * @return La couleur de la bordure du motif.
	 */
	public Color getCouleurBordure()
	{
		return couleurBordure;
	}
	
	
	/**
	 * Cette méthode permet de modifier la couleur de la bordure du motif.
	 * 
	 * @param couleurBordure Nouvelle valeur de la couleur de la bordure du motif.
	 */
	public void setCouleurBordure(Color couleurBordure)
	{
		this.couleurBordure = couleurBordure;
	}
	
	
	/**
	 * Retourne la couleur du motif.
	 * 
	 * @return La couleur du motif.
	 */
	public Color getCouleur()
	{
		return couleur;
	}
	
	
	/**
	 * Cette méthode permet de modifier la couleur du motif.
	 * 
	 * @param couleur Nouvelle valeur de la couleur du motif.
	 */
	public void setCouleur(Color couleur)
	{
		this.couleur = couleur;
	}
	
	
	/**
	 * Retourne l'opacité du motif.
	 * 
	 * @return L'opacité du motif.
	 */
	public float getOpaciteCouleur()
	{
		return opacite;
	}
	
	
	/**
	 * Cette méthode permet de modifier l'opacité du motif.
	 * 
	 * @param opaciteCouleur Nouvelle valeur de l'opacité du motif.
	 */
	public void setOpaciteCouleur(float opaciteCouleur)
	{
		this.opacite = opaciteCouleur;
	}
	
	
	/**
	 * Retourne l'opacité de la bordure du motif.
	 * 
	 * @return L'opacité de la bordure du motif.
	 */
	public float getOpaciteBordure()
	{
		return opaciteBordure;
	}
	
	
	/**
	 * Cette méthode permet de modifier l'opacité de la bordure du motif.
	 * 
	 * @param opaciteBordure Nouvelle valeur de l'opacité de la bordure du motif.
	 */
	public void setOpaciteBordure(float opaciteBordure)
	{
		this.opaciteBordure = opaciteBordure;
	}
	
	
	/**
	 * Retourne vrai si le motif est sélectionné, sinon faux.
	 * 
	 * @return Vrai si le motif est sélectionné, sinon faux.
	 */
	public boolean getSelectionne()
	{
		return selectionne;
	}
	
	
	/**
	 * Cette méthode permet de modifier si le motif est sélectionné ou non.
	 * 
	 * @param selectionne Nouvelle voleur de <i>selectionne</i>.
	 */
	public void setSelectionne(boolean selectionne)
	{
		this.selectionne = selectionne;
	}
	
	
	/**
	 * Retourne la poignée que l'utilisateur s'est servi pour redimensionner le motif.
	 * 
	 * @return La poignée que l'utilisateur s'est servi pour redimensionner le motif.
	 */
	public int getSelectedHandle()
	{
		return poigneeSelectionne;
	}
	
	
	/**
	 * Cette méthode permet de modifier la poignée que l'utilisateur s'est servi pour redimensionner le motif.
	 * 
	 * @param selectedHandle Nouvelle poignée que l'utilisateur s'est servi pour redimensionner le motif.
	 */
	public void setSelectedHandle(int selectedHandle)
	{
		this.poigneeSelectionne = selectedHandle;
	}
}	